/*
 * @(#)DependencyManager.java $version 2013. 11. 28.
 *
 * Copyright 2007 NHN Corp. All rights Reserved. 
 * NHN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */
package com.i2.june.dependency.model;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author kimseongjune@nhn.com
 */
public class DependencyManager {
	private String contextPath;
	private Map<String, DependencyModel> fileSystemMap;

	public void buildFileSystemMap(String contextPath) throws Exception {
		this.contextPath = contextPath;
		fileSystemMap = new HashMap<String, DependencyModel>();

		File root = new File(contextPath);
		buildFileSystemMap(root, fileSystemMap);
	}

	private void buildFileSystemMap(File file, Map<String, DependencyModel> resultMap) throws IOException {
		if (file.isHidden()) {
			return;
		} else if (file.isDirectory()) {
			File[] subFiles = file.listFiles();
			buildFileSystemMap(subFiles, resultMap);
		} else {
			String validFullPath = file.getCanonicalPath();
			resultMap.put(validFullPath, DependencyModel.create(validFullPath));
		}
	}

	private void buildFileSystemMap(File[] files, Map<String, DependencyModel> resultMap) throws IOException {
		for (File each : files) {
			buildFileSystemMap(each, resultMap);
		}
	}

	public String getContextPath() {
		return contextPath;
	}

	public Map<String, DependencyModel> getFileSystemMap() {
		return fileSystemMap;
	}

	public void buildDependency() {
		Iterator<String> iter = fileSystemMap.keySet().iterator();
		
		while (iter.hasNext()) {
			String key = iter.next();
			DependencyModel each = fileSystemMap.get(key);
			
			addDependency(each);
		}
	}

	private void addDependency(DependencyModel importingModel) {
		if (!importingModel.getId().endsWith("ftl")) {
			return;
		}
		
		BufferedReader br = null;
		InputStreamReader isr = null;
		FileInputStream fis = null;
		
		try {
			fis = new FileInputStream(importingModel.getId());
			isr = new InputStreamReader(fis, "UTF-8");
			br = new BufferedReader(isr);
			
			String line = "";
			
			while ((line = br.readLine()) != null) {
				addCssFileDependency(importingModel, line);
				addJsFilelDependency(importingModel, line);
				addFtlFileDependency(importingModel, line);
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				if (fis != null) {
					fis.close();
				}
				if (isr != null) {
					isr.close();
				}
				if (br != null) {
					br.close();
				}
			} catch (Exception e) {
//				e.printStackTrace();
				System.out.println("june log : file error!");
			}
		}
	}

	private void addFtlFileDependency(DependencyModel importingModel, String line) throws Exception {
		Pattern pattern = Pattern.compile("(.*)<#include(.*)\"(.*?\\.ftl).*?\"");
		
		Matcher matcher = pattern.matcher(line);
		
		if (matcher.find()) {
			String importedPath = matcher.group(3);
			importedPath = getRealPath(importedPath, importingModel);
			
			addDependency(importingModel, importedPath, line);
		}
	}

	private void addJsFilelDependency(DependencyModel importingModel, String line) throws Exception {
		Pattern pattern = Pattern.compile("(.*)<script(.*)src=\"(.*?\\.js).*?\"");
		
		Matcher matcher = pattern.matcher(line);
		
		if (matcher.find()) {
			String filePath = matcher.group(3);
			filePath = getRealPath(filePath, importingModel);
			
			addDependency(importingModel, filePath, line);
		}
	}

	private void addCssFileDependency(DependencyModel importingModel, String line) throws Exception {
		if (!line.contains("type=\"text/css\"")) {
			return;
		} else {
			Pattern pattern = Pattern.compile("(.*)<link(.*)href=\"(.*?\\.css).*?\"");
			
			Matcher matcher = pattern.matcher(line);
			
			if (matcher.find()) {
				String importedFilePath = matcher.group(3);
				importedFilePath = getRealPath(importedFilePath, importingModel);
				
				addDependency(importingModel, importedFilePath, line);
			}
		}
	}
	
	private void addDependency(DependencyModel importingModel, String importedFilePath, String line) {
		DependencyModel importedModel = fileSystemMap.get(importedFilePath);
		if (importedModel != null) {
			importingModel.addDependency(importedModel);
		} else {
			System.out.println(line + " has problems");
		}
	}

	private String getRealPath(String importedPath, DependencyModel importingModel) throws IOException, Exception {
		importedPath = replaceDirtyInfo(importedPath);
		
		if (WebResourcePathUtils.isWebResourceAbsolutePath(importedPath)) {
			return WebResourcePathUtils.convertWebResourceAbsolutePathToRealAbsolutePath(contextPath, importedPath);
		} else {
			return WebResourcePathUtils.makeAbsolutePath(importedPath, importingModel.getId());
		}
	}
	
	private String replaceDirtyInfo(String importedPath) throws Exception {
		importedPath = importedPath.replace("/event/", "/");
		importedPath = importedPath.replace("${base}/", "/");
		importedPath = importedPath.replace("${base!}/", "/");
		
		return importedPath;
	}

	public void printDependencyTree() {
		Iterator<String> iter = fileSystemMap.keySet().iterator();
		
		while (iter.hasNext()) {
			String key = iter.next();
			DependencyModel each = fileSystemMap.get(key);
			
			System.out.println(each.getId() + " : " + each.getDependencyCount());
		}
	}

	public boolean canRemove(String id) {
		DependencyModel dm = fileSystemMap.get(id);
		
		Map<String, DependencyModel> updatedMap = dm.simulateRemove();
		
		Iterator<String> iter = updatedMap.keySet().iterator();
		
		while (iter.hasNext()) {
			String key = iter.next();
			DependencyModel each = updatedMap.get(key);
			
			if (each.getDependencyCount() > 0) {
				return false;
			}
		}
		
		return true;
	}

	public Map<String, DependencyModel> getDependencyRootMap() {
		Map<String, DependencyModel> dependencyRootMap = new HashMap<String, DependencyModel>();
		
		Iterator<String> iter = fileSystemMap.keySet().iterator();
		
		while (iter.hasNext()) {
			String key = iter.next();
			DependencyModel each = fileSystemMap.get(key);
			
			if (each.getDependencyCount() == 0) {
				dependencyRootMap.put(each.getId(), each);
			}
		}
		
		return dependencyRootMap;
	}
}
